﻿using System;
using System.Collections.Generic;
using System.Data.Entity.Infrastructure;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GlobalObjects;


namespace DataAccessLayer
{
   public class DeviceEntity
    {
       /// <summary>
       /// updates the connection string and the database name of the given device id
       /// </summary>
       /// <param name="deviceId">device id</param>
       /// <param name="connection">connection string</param>
       /// <param name="database">database name</param>
       public void setConnectionStringAndDB_DAL(int deviceId, String connection, string database){
           using (var context = new MSR2DBEntities())
           {
               var query = from d in context.devices
                           where d.id == deviceId
                           select d;
               if (query.Count() == 0)
               {
                   InvalidOperationException exc = new InvalidOperationException("Device id doesn't exist");
                   throw exc;
               }
               else
               {
                   var deviceX = query.First();
                   deviceX.connection_string = connection;
                   deviceX.db_name = database;
               }
               context.SaveChanges();
           }
       }
       /// <summary>
       /// returns the connection string and the data base name of the given device id
       /// </summary>
       /// <param name="deviceId">device id</param>
       /// <returns>associated connection string and data base name of the given device id</returns>
       public Dictionary<string,string> getConnectionStringAndDB_DAL(int deviceId) {
           Dictionary<string,string> list = new Dictionary<string,string>();
           using (var context = new MSR2DBEntities())
           {
               var query = from d in context.devices
                           where d.id == deviceId
                           select d;
               if (query.Count() == 0)
               {
                   //error
                   InvalidOperationException exc = new InvalidOperationException("device id cannot be found");
                   throw exc;
               }
               else
               {
                   foreach (var x in query)
                   {
                       if (x.connection_string == "default" && x.db_name == "default")
                       {
                           list.Add("connectionString", Mongo.defaultConnectionString);
                           list.Add("databaseName", Mongo.defaultDatabase);
                       }
                       else if (x.connection_string == null && x.db_name == null)
                       {
                           list.Add("connectionString", Mongo.defaultConnectionString);
                           list.Add("databaseName", Mongo.defaultDatabase);
                       }
                       else
                       {
                           list.Add("connectionString", x.connection_string);
                           list.Add("databaseName", x.db_name);
                       }
                   }
               }
           }
           return list;
       }

       /// <summary>
       /// returns all the accessible devices for the given user id
       /// </summary>
       /// <param name="userId">user id</param>
        /// <returns>DeviceDiscovery object with three lists inside</returns>
       public DeviceDiscovery getAllAccessibleDevice_DAL(int userId) {
           DeviceDiscovery list = new DeviceDiscovery();
           list.Private = new List<DeviceDis>();
           list.Public = new List<DeviceDis>();
           list.Shared = new List<DeviceDis>();
           List<int> deviceIdList = new List<int>(); 
           using (var context = new MSR2DBEntities())
           {
               var privatequery = from d in context.devices
                           where d.user_id == userId
                           select d;
               foreach (var x in privatequery)
               {
                   DeviceDis device = new DeviceDis();
                   device.Id = x.id;
                   deviceIdList.Add(x.id);
                   device.Name = x.name;
                   device.Location = x.location;
                   device.Description = x.description;
                   device.Ip = x.ip;
                   device.Longlat = x.longlat;
                   list.Private.Add(device);
               }
               var publicquery = from d in context.devices
                                 where d.privacy == "public"
                                 select d;
               foreach (var x in publicquery)
               {    
                   Boolean exist = false;
                   foreach (int i in deviceIdList) { 
                       if(x.id==i){
                           exist = true;
                       }
                   }
                   if(exist == false){
                       DeviceDis device = new DeviceDis();
                       device.Id = x.id;
                       deviceIdList.Add(x.id);
                       device.Name = x.name;
                       device.Location = x.location;
                       device.Description = x.description;
                       device.Ip = x.ip;
                       device.Longlat = x.longlat;
                       list.Public.Add(device);
                   }
               }
               var sharedquery = from d in context.devices
                                 join s in context.shareds on d.id equals s.device_id
                                 where s.user_id == userId
                                 select d;
               foreach (var x in sharedquery)
               {
                   Boolean exist = false;
                   foreach (int i in deviceIdList)
                   {
                       if (x.id == i)
                       {
                           exist = true;
                       }
                   }
                   if (exist == false)
                   {
                       DeviceDis device = new DeviceDis();
                       device.Id = x.id;
                       deviceIdList.Add(x.id);
                       device.Name = x.name;
                       device.Location = x.location;
                       device.Description = x.description;
                       device.Ip = x.ip;
                       device.Longlat = x.longlat;
                       list.Shared.Add(device);
                   }
               }

           }
           return list;
       }
       /// <summary>
       /// checks if the given device shares data with the given user
       /// </summary>
       /// <param name="deviceID">device id</param>
       /// <param name="userID">user id</param>
       /// <returns>true if they share data, false otherwise</returns>
       public Boolean isShared_DAL(int deviceID, int userID) {
           Boolean shared = false;
           using (var context = new MSR2DBEntities())
           {
               var query = from s in context.shareds
                           where s.device_id == deviceID
                           select s;
               int count = query.Count();
               foreach (var x in query)
               {
                   if(x.user_id==userID){
                       shared = true;
                   }
               }
           }
           return shared;
       }

       public Device updateDeviceDetails_DAL(Device modifiedDevice)
       {
           Device updatedDevice = new Device();
           using (var context = new MSR2DBEntities())
           {
               var query = from s in context.devices
                           where s.id == modifiedDevice.Id
                           select s;
               if (query.Count() == 0)
               {
                   InvalidOperationException exc = new InvalidOperationException();
                   throw exc;
               }
               else
               {
                   var deviceX = query.First();
                   updatedDevice.Id = modifiedDevice.Id;
                   //
                   if (modifiedDevice.User_id != 0)
                   {
                       try
                       {
                           deviceX.user_id = modifiedDevice.User_id;
                           updatedDevice.User_id = modifiedDevice.User_id;
                           context.SaveChanges();
                       }
                       catch (System.Data.Entity.Infrastructure.DbUpdateException e)
                       {
                           throw e;
                       }
                   }
                       else
                       {
                           updatedDevice.User_id = deviceX.user_id.Value;
                       }
                   if (modifiedDevice.APIKey != null)
                   {
                       deviceX.api_key = modifiedDevice.APIKey;
                       updatedDevice.APIKey = modifiedDevice.APIKey;
                   }
                       else {
                           updatedDevice.APIKey = deviceX.api_key;
                       }

                   if (modifiedDevice.Name != null)
                   {
                       deviceX.name = modifiedDevice.Name;
                       updatedDevice.Name = modifiedDevice.Name;
                   }
                       else
                       {
                           updatedDevice.Name = deviceX.name;
                       }
                   if (modifiedDevice.Location != null)
                   {
                       deviceX.location = modifiedDevice.Location;
                       updatedDevice.Location = modifiedDevice.Location;
                   }
                       else
                       {
                           updatedDevice.Location = deviceX.location;
                       }

                   if (modifiedDevice.Description != null)
                   {
                       deviceX.description = modifiedDevice.Description;
                       updatedDevice.Description = modifiedDevice.Description;
                   }
                       else
                       {
                           updatedDevice.Description = deviceX.description;
                       }
                   if (modifiedDevice.Privacy != null)
                   {
                       deviceX.privacy = modifiedDevice.Privacy;
                       updatedDevice.Privacy = modifiedDevice.Privacy;
                   }
                       else
                       {
                           updatedDevice.Privacy = deviceX.privacy;
                       }
                   if (modifiedDevice.Ip != null)
                   {
                       deviceX.ip = modifiedDevice.Ip;
                       updatedDevice.Ip = modifiedDevice.Ip;
                   }
                       else
                       {
                           updatedDevice.Ip = deviceX.ip;
                       }
                   if (modifiedDevice.Status != false)
                   {
                       deviceX.status = modifiedDevice.Status;
                       updatedDevice.Status = modifiedDevice.Status;
                   }
                       else
                       {
                           updatedDevice.Status = deviceX.status.Value;
                       }
                   if (modifiedDevice.Confirmed != false)
                   {
                       deviceX.confirmed = modifiedDevice.Confirmed;
                       updatedDevice.Confirmed = modifiedDevice.Confirmed;
                   }
                       else
                       {
                           updatedDevice.Confirmed = deviceX.confirmed.Value;
                       }
                   if (modifiedDevice.Creation_date != null)
                   {
                       deviceX.creation_date = modifiedDevice.Creation_date;
                       updatedDevice.Creation_date = modifiedDevice.Creation_date;
                   }
                       else
                       {
                           updatedDevice.Creation_date = deviceX.creation_date.Value;
                       }
                   if (modifiedDevice.Confirmation_date != null)
                   {
                       deviceX.confirmation_date = modifiedDevice.Confirmation_date;
                       updatedDevice.Confirmation_date = modifiedDevice.Confirmation_date;
                   }
                       else
                       {
                           updatedDevice.Confirmation_date = deviceX.confirmation_date.Value;
                       }
                   if (modifiedDevice.Longlat != null)
                   {
                       deviceX.longlat = modifiedDevice.Longlat;
                       updatedDevice.Longlat = modifiedDevice.Longlat;
                   }
                       else
                       {
                           updatedDevice.Longlat = deviceX.longlat;
                       }
                   if (modifiedDevice.Connection_string != null)
                   {
                       deviceX.connection_string = modifiedDevice.Connection_string;
                       updatedDevice.Connection_string = modifiedDevice.Connection_string;
                   }
                   else
                   {
                       updatedDevice.Connection_string = deviceX.connection_string;
                   }

                   if (modifiedDevice.Db_name != null)
                   {
                       deviceX.db_name = modifiedDevice.Db_name;
                       updatedDevice.Db_name = modifiedDevice.Db_name;
                   }
                   else
                   {
                       updatedDevice.Db_name = deviceX.db_name;
                   }

                   context.SaveChanges();
               }
           }
           return updatedDevice;
       }

       /// <summary>
       /// returns the longlat of the specified device
       /// </summary>
       /// <param name="deviceId">id of the device</param>
       /// <returns>longlat</returns>
       public string getLongLatById_DAL(int deviceId){
       string longlat="";
       using (var context = new MSR2DBEntities())
       {
           var query = from d in context.devices
                       where d.id == deviceId
                       select d;
           if (query.Count() == 0)
           {
               InvalidOperationException exc = new InvalidOperationException();
               throw exc;
           }
           else {
               longlat = query.First().longlat;
           }
       }

       return longlat;
       }
       /// <summary>
       /// adds the new ip address to the specified device
       /// </summary>
       /// <param name="deviceId">id of the device</param>
       /// <param name="ip">new ip address</param>
       public Device addIpAddress_DAL(int deviceId, string ip){
           Device device = new Device();
          using (var context = new MSR2DBEntities())
          {
          var query = from d in context.devices
                      where d.id == deviceId
                      select d;
                if (query.Count() == 0)
                {
                    InvalidOperationException exc = new InvalidOperationException();
                    throw exc;
                }
                else {
                    var devicex =  query.First();
                        //
                    device.Id = devicex.id;
                    device.User_id = devicex.user_id.Value;
                    device.APIKey = devicex.api_key;
                    device.Name = devicex.name;
                    device.Location = devicex.location;
                    device.Description=devicex.description;
                    device.Privacy = devicex.privacy;
                    if (ip == null)
                    {
                        device.Ip = devicex.ip;
                    }
                    else {
                        device.Ip = ip;
                    }
                    device.Status = devicex.status.Value;
                    device.Confirmed = devicex.confirmed.Value;
                    device.Confirmation_date = devicex.confirmation_date.Value;
                    device.Creation_date = devicex.creation_date.Value;
                    device.Longlat = devicex.longlat;
                    context.SaveChanges();
                }
           }
          return device;
       }
    
       /// <summary>
       /// returns true if the given apikey and deviceid belong to a same device
       /// </summary>
       /// <param name="apikey"></param>
       /// <param name="deviceId"></param>
       /// <returns></returns>
      public Boolean APIKeyAndDeviceIdMatch_DAL(string apikey,int deviceId){
          Boolean match = false;
          using (var context = new MSR2DBEntities())
          {
          var query = from d in context.devices
                           where d.id == deviceId && d.api_key==apikey
                           select d;
          if (query.Count() == 1) {
              match = true;
          }
          }
          return match;
      }
       /// <summary>
       /// deletes a specific Device from the database
       /// </summary>
       /// <param name="deviceid"></param>
       /// <returns></returns>
       public Boolean deleteDeviceById_DAL(int deviceid) {
           Boolean deleted = false;
           Boolean exist = false;
           using (var context = new MSR2DBEntities())
           {
               var query = from d in context.devices
                           where d.id == deviceid
                           select d;
               if (query.Count() == 1) {
                   exist = true;
                   //
                   var query1 = from s in context.sensors
                               where s.device_id == deviceid
                               select s;
                   foreach(var sen in query1){
                       //
                       var queryAccess = from a in context.sensor_access_time
                                         where a.sensor_id == sen.id
                                         select a;
                       if (queryAccess.Count() != 0)
                       {
                           context.sensor_access_time.Remove(queryAccess.First());
                       }
                       //
                       //
                       //var queryNotification = from n in context.notifications
                       //             where n.sensor_id == sen.id
                       //             select n;
                       //if (queryNotification.Count() != 0)
                       //{
                       //    foreach (var not in queryNotification)
                       //    {
                       //        context.notifications.Remove(not);
                       //    }
                       //}
                       //
                       context.sensors.Remove(sen);
                   }
                   var query3 = from s in context.shareds
                                where s.device_id == deviceid
                                select s;
                   foreach (var dev in query3)
                   {
                       context.shareds.Remove(dev);
                   }
                   //
                   var query4 = from n in context.notifications
                                where n.device_id == deviceid
                                select n;
                   if (query4.Count() != 0)
                   {
                       foreach (var not in query4)
                       {
                           context.notifications.Remove(not);
                       }
                   }
                   //
                   var devicex = query.First();
                   context.devices.Remove(devicex);
                   context.SaveChanges();
               }
               var query2 = from dd in context.devices
                           where dd.id == deviceid
                           select dd;
               if (query2.Count()==0 && exist)
               {
                   deleted = true;
               }
           }
           return deleted;
       }

       /// <summary>
       /// returns a list of device id associated to the given user id 
       /// </summary>
       /// <param name="userid"></param>
       /// <returns>a list of device id</returns>
       public List<int> getDeviceIdByUserId_DAL(int userid) {
           List<int> list = new List<int>();
           using (var context = new MSR2DBEntities())
           {
               var query = from d in context.devices
                           where d.user_id == userid
                           select d;
               foreach (var x in query) {
                   list.Add(x.id);
               }
           }
           return list;
       }

       /// <summary>
       /// chekcks whether the given APIkey exist in the Database or not
       /// </summary>
       /// <param name="apikey"></param>
       /// <returns>false if doesnt exist, true otherwise</returns>
       public Boolean checkApikeyExist_DAL(string apikey)
       {
           Boolean exist = false;
           using (var context = new MSR2DBEntities())
           {
               var query = from d in context.devices
                           where d.api_key == apikey
                           select d;
               if (query.Count() != 0)
               {
                   exist = true;
               }
               return exist;
           }
       }
       /// <summary>
       /// return a Device object based on the given userId and deviceId. by considering the 
       /// access level of the device (private, public)
       /// </summary>
       /// <param name="UserID">Id of the user who is wants to have access to the device</param>
       /// <param name="deviceId">Id of the required device</param>
       /// <returns>Device object</returns>
       public Device getDeviceById_DAL(int UserID, int deviceId){
           Device device = new Device();
           Boolean idExist = false;
           Boolean isOwner = false;
           Boolean isPublic = false;
           Boolean isShared = false;
           using (var context = new MSR2DBEntities())
           {
               var query = from d in context.devices
                           where d.id == deviceId
                           select d;
               if (query.Count() != 0)
               {
                    var devicex = query.First(); 
                    idExist=true;
                    if (idExist)
                    {
                        if (devicex.user_id == UserID)
                        {
                            isOwner = true;
                        }
                        if (devicex.privacy == "public")
                        {
                            isPublic = true;
                        }
                        if (isPublic == false && isOwner == false)
                        {
                            isShared = isShared_DAL(deviceId, UserID);
                        }
                    }
                    if (isOwner || isPublic || isShared)
                    {
                        device.Id = devicex.id;
                        device.User_id = devicex.user_id.Value;
                        device.APIKey = devicex.api_key;
                        device.Name = devicex.name;
                        device.Location = devicex.location;
                        device.Description = devicex.description;
                        device.Privacy = devicex.privacy;
                        device.Ip = devicex.ip;
                        device.Status = devicex.status.Value;
                        device.Confirmed = devicex.confirmed.Value;
                        device.Creation_date = devicex.creation_date.Value;
                        device.Confirmation_date = devicex.confirmation_date.Value;
                        device.Longlat = devicex.longlat;
                        device.Connection_string = devicex.connection_string;
                        device.Db_name = devicex.db_name;
                        return device;
                    }
                    else {
                        InvalidOperationException exc = new InvalidOperationException("authorisation declined");
                        throw exc;
                    }
               }
               else{
                    InvalidOperationException exc =  new InvalidOperationException("id cannot be found");
                    throw exc;
               }
            }
       }

       /// <summary>
       /// checks whether the given name for the device already exist for the specific user
       /// </summary>
       /// <param name="user_id">user id</param>
       /// <param name="deviceName">new name</param>
       /// <returns>true if exist, false if it doesnt exist</returns>
       public Boolean nameExist_DAL(int user_id, string deviceName) {
           Boolean exist =  true;
           using (var context = new MSR2DBEntities())
           {
               var query = from d in context.devices
                           where d.user_id == user_id && d.name == deviceName 
                           select d;
               if (query.Count() == 0)
               {
                   exist = false;
               }
           }                 
           return exist;
       }
       /// <summary>
       /// ckecks whether the given device id exist in the database or not
       /// </summary>
       /// <param name="device_id">id to check</param>
       /// <returns>true if the id exist. false if it doesn't</returns>
       public Boolean idExist_DAL(int device_id)
       {
           Boolean exist = true;
           using (var context = new MSR2DBEntities())
           {
               var query = from d in context.devices
                           where d.id == device_id
                           select d;
               if (query.Count() == 0)
               {
                   exist = false;
               }
           }
           return exist;
       }
       /// <summary>
       /// adds a new Device to the devices table
       /// </summary>
       /// <param name="newDevice">represent the data of the new record in the table</param>
       /// <returns></returns>
       public Device addDevice_DAL(Device newDevice)
       {
           try
           {
               if (nameExist_DAL(newDevice.User_id, newDevice.Name))
               {
                   InvalidOperationException exc = new InvalidOperationException();
                   throw exc;
               }
               else
               {
                   string newIp;
                   if (newDevice.Ip == null)
                   {
                       newIp = "0.0.0.0";
                   }
                   else {
                       newIp = newDevice.Ip;
                   }
                   Device createdDevice = new Device();
                   String key = Guid.NewGuid().ToString();
                   Boolean isNotUnique = true;
                   using (var context = new MSR2DBEntities())
                   {
                       while (isNotUnique)
                       {
                           var query = from d in context.devices
                                       where d.api_key == key
                                       select d;
                           if (query.Count() == 0)
                           {
                               isNotUnique = false;
                           }
                           else
                           {
                               key = Guid.NewGuid().ToString();
                           }
                       }

                       var ndevice = new device()
                       {
                           user_id = newDevice.User_id,
                           api_key = key,
                           name = newDevice.Name,
                           location = newDevice.Location,
                           description = newDevice.Description,
                           privacy = newDevice.Privacy,
                           ip = newIp,
                           status = false,
                           confirmed = false,
                           creation_date = newDevice.Creation_date,
                           confirmation_date = newDevice.Confirmation_date,
                           longlat = newDevice.Longlat,
                           connection_string = Mongo.defaultConnectionString,
                           db_name = Mongo.defaultDatabase,

                       };
                       context.devices.Add(ndevice);
                       context.SaveChanges();

                       createdDevice.Id = ndevice.id;
                       if (ndevice.user_id.HasValue)
                       {
                           createdDevice.User_id = ndevice.user_id.Value;
                       }
                       createdDevice.APIKey = ndevice.api_key;
                       createdDevice.Name = ndevice.name;
                       createdDevice.Location = ndevice.location;
                       createdDevice.Description = ndevice.description;
                       createdDevice.Privacy = ndevice.privacy;
                       createdDevice.Ip = ndevice.ip;
                       createdDevice.Status = ndevice.status.Value;
                       createdDevice.Confirmed = ndevice.confirmed.Value;
                       createdDevice.Creation_date = newDevice.Creation_date;
                       createdDevice.Confirmation_date = newDevice.Creation_date;
                       createdDevice.Longlat = newDevice.Longlat;
                       createdDevice.Connection_string = newDevice.Connection_string;
                       createdDevice.Db_name = newDevice.Db_name;

                       Console.WriteLine("New device is added successfully- ID is:" + ndevice.id);
                       return createdDevice;
                   }
               }
           }
           catch (DbUpdateException sqle)
           {
               Console.WriteLine("Error in addDevice");
               Debug.Print("Error in addDevice(), " + sqle.ToString());
               throw sqle;
           }
       }

    }
}
